package shared.downloader

import shared.runtime._
import java.io._
import shared.file._

class Downloader(val runtime: Runtime) extends IOAware {
  import org.jgroups.Address
  import shared.file._
  
  def channel = runtime.channel
  
  def fileInfo(file: String, add: Address): Option[RemoteFile] = {
    val req = new FileInfoRequest (file)
    val response = channel.sendWait(add, req)(runtime.timeout)
    response.flatMap(_.info)
  }
  
  def fileInfo(file: String, add: AddressInformation):Option[RemoteFile] = fileInfo(file, add.add)
  
  def getFile(m: FileInfoMessage, add: Address):Option[InputStream] = {
    import java.net._
    m.info.map { file =>
      val inet = add.asInstanceOf[org.jgroups.stack.IpAddress].getIpAddress
      val socket = new Socket(inet, m.port)
      val writer = new OutputStreamWriter(socket.getOutputStream, "UTF-8")
      writer.write(file.path)
      writer.write("\n")
      writer.flush
      socket.getInputStream
    }
  }

  def transferFile(m: FileInfoMessage, add: Address) (dst: File): Boolean = {
    val in = getFile(m, add)
    val ft = m.info.map { info => runtime.downloading.add(info.name, info.size) }
    in.map{ 
      input =>
      val out = new FileOutputStream(dst)
      eachChunk(input) { (data, len) =>
        ft.get.add(len)
        out.write(data, 0, len)
      }
    }
    in.isDefined
  }
  
  def saveToFolder(m: FileInfoMessage, add: Address) (folder: File): Boolean = {
    m.info.map { ref =>
      val tgt = new File(folder, ref.name)
      if(tgt.exists) {
         false
      } else {
        transferFile(m, add)(tgt)
      }
    }.isDefined
  }
  
  def saveToFolderKeepInfo(m: FileInfoMessage, add: Address) (folder: SharedFolder): SharedFolder = {
    if(saveToFolder(m, add)(folder.root)) {
      val r = folder.refresh
      val info = m.info.get
      val nf = r.getFile(info.path)
      if(nf.get.md5 != info.md5) {
        //something bad happened. Download it again
        saveToFolderKeepInfo(m, add)(folder)
      } else {
        nf.get.keywords = info.keywords
        nf.get.description = info.description
        r        
      }
    } else {
      folder      
    }
  }
  
  def save(m: FileInfoMessage, add: Address) = {
    runtime.dropDirectory.map { d =>
      saveToFolderKeepInfo(m, add) (d)
    }
  }
  
  def transferTask(m: FileInfoMessage, add: Address) (file: File) = {
    getFile(m, add).map { in =>
      new TransferTask(in, new FileOutputStream(file), m.info.get.size)
    }
  }
  
}
